Kaggle 的 Object Detection 比赛 Data Science Bowl 总结
比赛简介
Kaggle的一个目标检测类比赛, 我们也是第一次参加 CV 方向的比赛, 这篇博客主要总结一下和比赛相关的基础知识. 比赛给定一些细胞核的图片, 目标是检测图片中的细胞核的位置, 采用 IOU 作为评价指标, 我们尝试过 U-net 但是没能调到一个较优解, 最终使用的是 Kaiming He 大神的 Mask RCNN, 这个比赛最终我们侥幸拿到了一块银牌(170/3634, Top 5%).
目标检测
CNN 中的基本概念
CNN 的强大之处在于它的多层结构能自动学习特征, 并且可以学习到多个层次的特征:
1. 较浅的卷积层感知域较小, 学习到一些局部区域的特征;
2. 较深的卷积层具有较大的感知域, 能够学习到更加抽象一些的特征.
卷积核 (kernel)
卷积核决定如何挖掘输入数据的 2D 局部特征, 例如 3*3 就是每九个单元格内按照矩阵乘法获取对应位置的输出, 卷积核的大小确定输出的 feature map 尺寸, 如果是 1*1 的卷积核则输入输出尺寸相同, 可以用于升降维, 如下图所示.
在 Pytorch 中, 卷积网络的输入为 $(C_{in}, C_{out}, (\text{kernel_size e.g. (3, 3)}), stride=1, padding=0)$, 要求输入格式为 $(\text{batch_size}, C_{in}, H, W)$, 那么输出的图片 size 满足:
一个卷积核的工作过程: 假设输入图像 size 为 $(H, W) = 256\times256$, 输入通道数为 $C_{in} = 72$,即输入张量为 $(1, 72, 256, 256)$; 我们选择输出通道 $C_{out} = 36$, 采用 3*3 大小的卷积核, 步长 stride 选择为 1, padding 参数为不填充即 (0, 0), 则输出为 $(1, 36, 254, 254)$
通道 (channel)
通道数可以理解为图片的第 3 维度, 它决定了对于同一个大小的卷积核, 我们需要做多少次参数不同的矩阵乘法来提取特征, 即选定了输出通道数, 就有多少组卷积核参数. 仍以上图为例, 输入维度是 4*4*3 表示其尺寸为 4*4, 通道数为 3, 经过 1*1 的卷积核后尺寸保持不变, 根据选择的输出通道数决定如何得到输出, 如输出通道数选择 2 则是降维, 为 4 则是升维.
AlexNet 和 VGG
AlexNet: 采用 5 个卷积层和 3 个全连接层, 使用 ReLU 激活函数解决梯度消失问题, 并且在每个全连接层后都接上了 dropout 以减少过拟合. 此外, AlexNet 的一个创新是 LRN (Local Response Normalization) 局部响应归一化, LRN模拟神经生物学上一个叫做侧抑制 (lateral inhibitio) 的功能, 侧抑制指的是被激活的神经元会抑制相邻的神经元. LRN 局部响应归一化借鉴侧抑制的思想实现局部抑制, 使得响应比较大的值相对更大, 提高了模型的泛化能力. LRN只对数据相邻区域做归一化处理, 不改变数据的大小和维度.
VGG: VGG16(13 卷积层 + 3 全连接层) 相比 AlexNet 最大改进是采用连续的几个 3*3 卷积核替代 AlexNet 中的较大卷积核 (11*11, 5*5) 并且去掉了 LRN (作者发现 LRN 的作用不明显). 采用堆叠的小卷积核的原因是多层非线性层可以增加网络的深度学习更复杂的模式, 并且代价更小 (参数更少). VGG 网络,结构非常一致 从头到尾都是使用 3*3 的卷积核以及 2*2 的 max pooling. VGG 的缺点在于参数空间巨大, 内存占用较高速度较慢, 参数多的主要原因在于 VGG 使用了 3 个全连接层.
ALexNet 和 VGG 的输入尺寸都是 224*224
GoogleNet/Inception
VGG 在内存和时间上的计算要求很高. 由于卷积层的通道数过大, VGG 并不高效. 在卷积操作中, 输出特征图上某一个位置, 其是与所有的输入特征图是相连的, 这是一种密集连接结构. GoogLeNet 基于这样的理念: 在深度网路中大部分的激活值是不必要的 (为 0), 或者由于相关性是冗余的. 因此, 最高效的深度网路架构应该是激活值之间是稀疏连接的.
因此 GoogleNet 提出了 Inception 模块, 这个模块使用密集结构来近似一个稀疏的 CNN 如下图所示. GoogleNet 使用不同大小的卷积核获取不同大小的感受野, 同时采用了 1*1 的卷积核 (瓶颈层) 降维来降低计算量. 另外一个特殊设计之处在于最后的卷积层后使用了全局均值池化替代了全连接层, 进一步减少了参数量.
GoogleNet 的输入尺寸是 299*299
ResNet
从上面的几个模型可以看出随着网络层次的加深网络的精度是提升的, 但是需要注意过拟合. 而网络层次加深带来的其他问题: 一是梯度消失问题, 经过反向传播训练时较靠前的层梯度会很小, 训练几乎停滞; 二是优化困难, 参数空间随着深度增加也变大, 优化十分困难. ResNet 的提出使得我们可以训练更深的网络, 其关键的残差模块 (恒等快捷连接) 如下图所示.
深度网络的训练问题称为退化问题, 残差单元可以解决退化问题的背后逻辑在于此: 想象一个网络 A, 其训练误差为 x. 现在通过在 A 上面堆积更多的层来构建网络 B, 这些新增的层什么也不做, 仅仅复制前面 A 的输出. 这些新增的层称为 C. 这意味着网络 B 应该和 A 的训练误差一样. 那么, 如果训练网络 B 其训练误差应该不会差于 A. 但是实际上却是更差, 唯一的原因是让增加的层 C 学习恒等映射并不容易. 为了解决这个退化问题, 残差模块在输入和输出之间建立了一个直接连接, 这样新增的层 C 仅仅需要在原来的输入层基础上学习新的特征, 即学习残差, 会比较容易.
假设网络的输出为 $H$, 残差定义为 $H - x$, 那么 ResNet 其实就是在学习这个残差: $F = H - x$ 即图中的 $F(x)$, 通过将残差最小化来学习模型.
ResNet 也最后使用了全局均值池化层. 利用残差模块, 可以训练 152 层的残差网络. 其准确度比 VGG 和 GoogLeNet 要高, 计算效率也比 VGG 高. ResNet 主要使用 3*3 卷积, 这点与 VGG 类似. 在 VGG 基础上, 短路连接插入形成残差网络. 如下图所示, 虚线部分表示维度不匹配需要采用 1*1 卷积核来解决:
ResNet 的输入尺寸为 224*224
0、mAP评估指标?
mAP (mean Average Precision, 平均精度均值) 是目标检测指标, 在目标检测中, 每一类都可以根据 recall 和 precision 绘制 P-R 曲线, AP 就是该曲线下的面积, mAP 就是所有类 AP 的平均值.
目标检测中, TP 是 IOU>0.5 的检测框数量, FP 是 IOU<0.5 的检测框和对同一个 GT (Ground Truth, 真值框) 多余的检测框数量. FN 是没有被检测出来的GT的数量.
1、IOU是什么?用代码写一个IOU函数
IOU的意思是是交并比, 它定义了两个 bbox(bounding box) 的重叠度.
它的公式可以表示为:
1 | #并集 |
2、U-Net
传统 CNN 分类模型都是图像级别的分类, 而在语义分割 (semantic segmentation) 则是对图像进行像素级别的分类, 根据像素级的分类我们就可以从图像中分割出目标. 在介绍 U-Net 之前, 先引入经典的 FCN 有助于对 U-Net 的理解.
FCN
针对语义分割问题, FCN (Fully Convolutional Networks) 的提出成为语义分割的基本框架, 后续的很多算法都是在 FCN 的基础上改进的.
对于一般的分类 CNN 网络, 如 VGG 和 Resnet, 都会在网络的最后加入一些全连接层, 经过 softmax 后就可以获得类别概率信息. 但是这个概率信息是 1 维的, 即只能标识整个图片的类别, 不能标识每个像素点的类别, 所以这种全连接方法不适用于图像分割. 而 FCN 提出可以把后面几个全连接都换成卷积, 这样就可以获得一张 2 维的 feature map (heatmap), 获得每个像素点的分类信息 (不同尺度的 feature 融合后使用 softmax 分类), 损失函数为针对每个 pixel 计算 softmax loss, 如下图所示.
FCN 网络的原理如下图如示:
先通过卷积和池化操作的得到不同尺寸的 pool feature. 那么:
1. 对于 FCN-32s, 直接对 pool5 feature 进行 32 倍上采样获得 32x upsampled feature, 再对 32x upsampled feature 每个点做 softmax prediction 获得 32x upsampled feature prediction (即分割图)
2. 对于 FCN-16s, 首先对 pool5 feature 进行 2 倍上采样获得 2x upsampled feature, 再把 pool4 feature 和 2x upsampled feature 逐点相加, 然后对相加的 feature 进行 16 倍上采样, 并 softmax prediction, 获得 16x upsampled feature prediction.
3. 对于 FCN-8s, 首先进行 pool4+2x upsampled feature 逐点相加, 然后又进行 pool3+2x upsampled 逐点相加, 即进行更多次特征融合. 具体过程与16s类似.
如何保证逐点相加的 feature map 是一样大的呢?这就要引入 crop 层进行裁剪对齐.
下图是 FCN-32s、FCN-16s、FCN-8s 的实验对比, 可以看出使用多层feature融合有利于提高分割准确性
那么什么是上采样? 实际上上采样包括两种方式:
1. Resize, 如双线性插值缩放, 类似于图像缩放:
给定下图中四个已知坐标, $Q_{11}, Q_{12}, Q_{21}, Q_{22}$, 待插值点为 $P$, 双线性插值首先在 x 轴方向上对 $R_1, R_2$ 进行插值计算, 然后根据 $R_1, R_2$ 对 $P$ 进行插值计算.
其计算过程如下: 给定未知函数 $f$ 在 $Q_{11} = (x_1, y_1), Q_{12} = (x_1, y_2), Q_{21} = (x_2, y_1), Q_{22} = (x_2, y_2)$ 的值, 求 $f$ 在待插值点 $P$ 的值 $P = (x, y)$.
首先在 x 轴方向插值得到:
然后在 y 轴方向插值得到:
这样即得到所求 $f(x, y)$.
2. Deconvolution (Transposed Convolution) 反卷积:
反卷积就是对卷积操作的反转, 保持卷积核大小不变, 通过设置 padding 参数为 (2, 2), 步长 stride = 1 完成反卷积操作, 如下动图所示.
上采样的意义在于在于将小尺寸的高维度 feature map 恢复回去, 以便做 pixelwise prediction, 获得每个点的分类信息.
U-Net
有了 FCN 的基础, 再来看 U-Net. U-Net 和 FCN 一样都是很小的分割网络, 结构简单, 其结构示意图如下图所示.
U-Net 结构类似于一个大写的 U 字母: 首先进行 Conv + Pooling下采样; 然后 Deconv 反卷积进行上采样, crop 之前的低层 feature map 进行融合; 然后再次上采样. 重复这个过程, 直到获得输出 388x388x2 的 feature map, 最后经过 softmax 获得 output segment map. 总体来说与 FCN 思路非常类似.
那么 U-Net 和 FCN 的区别是什么? U-Net 采用了与 FCN 完全不同的特征融合方式: 拼接. 如下图所示, FCN 是逐点相加, U-Net 将特征按 channel 维度拼接, 形成更 “厚” 的特征.
后面的 CNN 图像语义分割也就基本上是这个套路:
1. 下采样+上采样: Convlution + Deconvlution/Resize
2. 多尺度特征融合: 特征逐点相加/特征按 channel 维度拼接
3. 获得像素级别的 segement map: 对每一个像素点进行判断类别
3、RCNN
要介绍 Mask RCNN, 首先就从最开始的 RCNN(Region-based CNN features) 开始, RCNN 将 CNN 的方法引入目标检测领域, 大大提高了检测效果, 改变了目标检测领域的主要研究思路. 其后的 Fast RCNN、Faster RCNN 以及 Mask RCNN 代表了该领域的最高水准.
RCNN 的思路主要分为四个步骤:
1、候选区域生成: 利用选择性搜索 (Selective Search) 算法从图像中提取 1k~2k 个左右可能是物体的候选区域 (region proposal); 关于选择性搜索算法, 其先将图像分割成小区域 (1k~2k), 然后按照一定的合并规则合并可能性最高的两个相邻区域直到图像只剩一个区域, 最后输出曾经存在过的区域.
2、特征提取: 把这些 region proposal 缩放成 227*227 的大小并用 5 层卷积两层全连接的 CNN 提取特征;
3、类别判断: 用 SVM 训练 CNN 提取出来的各类别特征, 每一类有一个 SVM 分类器;
4、位置精修: 对 SVM 分好类的 region proposal 做边框回归 (bbox regression), 用回归值来矫正建议窗口, 生成预测的窗口坐标. 关于边框回归, 其思想就是平移 + 缩放使得 bbox 更接近 ground truth, 其学习一组参数 , 使得输入的 bbox 经过回归变换后和 ground truth 非常接近, .
缺点:
(1) 训练分为多个阶段, 步骤繁琐: 基于预训练网络的微调 + 训练SVM + 训练边框回归器;
(2) 训练耗时, 占用磁盘空间大; 5000张图像产生几百G的特征文件;
(3) 速度慢:使用GPU, VGG16模型处理一张图像需要47s, 如果采用 AlexNet 精度则降低;
(4) 测试速度慢:每个候选区域需要运行整个前向CNN计算;
(5) SVM和回归是事后操作, 在SVM和回归过程中CNN特征没有被学习更新.
4、SPP Net
RCNN 的一大问题就是对于提取的每个 regional proposal 都要经过 CNN 前向传播一次, 计算量巨大. 为此, SPP(Spatial Pyramid Pooling) 则考虑既然 regional proposal 都是从原图像中提取的, 为什么不直接原图像输入 CNN 中, 在映射后的 feature maps 中提取 regional proposal, 通过这样做 SPP 大大减少了计算量.
一句话来说就是用 SPP layer 通过三个 Pooling 窗口 (下图中的蓝、绿、灰窗口, 形成了金字塔形状因此得名 SPP) 从 feature maps 提取出同样维度的特征向量. 之后 fast rcnn 的 RoI 层就是借鉴了 SPP Net 的这种思路.
一张图片放入 CNN 网络中经过多层转化后, 会生成多张(论文中是 256 个卷积核)特征图:
而在原图中任意一个窗口在这些特征图中都有对应的窗口, 假设图片中的某个位置坐标为 $(x, y)$, 则其在特征图上的位置为 $(x’, y’) = (\lfloor x/S \rfloor + 1, \lfloor y/S \rfloor + 1)$, 其中 $S$ 是所有卷积层的 strides 乘积.
SPP 就是在这些对应的窗口中做池化操作使每张特征图都生成 21 维的特征向量用于 SVM 分类以及 bbox 回归.
具体做法是, 在 conv5 层得到的特征图是 256 个, 每个都做一次 spatial pyramid pooling. 先把每个特征图分割成多个不同尺寸的网格, 网格分别为 4*4、2*2、1*1 ,然后每个网格做 max pooling, 这样 256 个特征图就形成了 16*256, 4*256, 1*256 维特征, 他们连起来就形成了一个固定长度的特征向量, 将这个向量输入到后面的全连接层.
5、Fast RCNN
RCNN、SPP Net 训练时, 提取 region proposal、CNN 特征提取、SVM 分类和 bbox 回归是互相隔离的, Fast RCNN 则有几个特点: 1) 特征都暂存在显存上不需要额外磁盘空间, 速度更快、精度更高; 2) 联合训练, 除了提取 region proposal 阶段, 原来的SVM 分类、bbox 回归都联合在 CNN 阶段同时训练, 将原来最后一层的 softmax 替换成区域分类 softmax (替代 SVM 分类) 和 bbox 微调以完成联合训练目标, 输入变为两个, 一个是原图像作为 CNN 输入, 另一个是选择搜索算法给出的可能 region proposal 直接映射到 Conv5 层上; 3) 提出了 RoI (Region of Interest) 层, 算是 SPP 的变种, 将原来的多尺度 (多个 Pooling 窗口) 映射换成单尺度映射.
Fast RCNN 的整体流程如下:
1、利用 selective search 算法在图像中从上到下提取2000个左右的候选窗口 (Region Proposal 也即是 RoI);
2、将整张图片输入 CNN, 进行特征提取;
3、把候选窗口映射到 CNN 的最后一层卷积 feature map 上;
4、通过 RoI pooling 层为每个候选窗口生成固定尺寸 (7*7) 的 RoI 特征向量;
5、利用 Softmax Loss (区域分类损失) 和 Smooth L1 Loss (边框回归损失) 对分类概率和边框回归联合训练.
相比R-CNN, 主要两处不同:
(1)最后一层卷积层后加了一个 RoI Pooling layer, RoI 是 SPP Net 的简化版本, SPP Net 对一个区域窗口进行 4*4, 2*2, 1*1 的 pooling, 也就是说一个区域对应的特征图经过 SPP Pooling 最终得到 21 维的特征向量; 而 RoI layer 只进行 7*7 的池化, 所以得到的是 49 维的特征向量. 这个特征向量随后通过 SVD 分解得到两个输出向量一个是 Softmax 分类得分, 另一个是 bbox 回归.
(2)损失函数使用了多任务损失函数 (multi-task loss), 将边框回归直接加入到 CNN 网络中训练.
6、Faster RCNN
Faster RCNN 可以简单概括成: faster rcnn = fast rcnn + rpn 层, faster rcnn 将 fast rcnn 中 RoI 的提取单独写成了一个 RPN (Region Proposal Network) 层. Faster RCNN 所有的卷积操作都有 padding 操作, 因此所有卷积操作都不会改变图的尺寸, 只有 max pooling 会把 featrue map 尺寸变为 1/2. Faster RCNN 的输入图片尺寸需要缩放到 800*600, 其流程如下:
1、将整个图片输入 CNN, 提取图像的 feature maps, 被后续的 RPN 和全连接层所共享;
2、通过 RPN 生成 region proposal, 通过 softmax 判断 anchors 属于 foreground 或 background 并用 bbox 回归修正 anchors 获取精确的 region proposal;
3、RoI Pooling, 根据 feature maps 和 region proposal 提取 proposal feature maps 传给全连接层测过判断类别;
4、根据 proposal feature maps 计算类别并再次使用 bbox 回归获取最终精确的预测位置.
RPN
Faster RCNN 抛弃选择搜索算法生成候选窗口, 使用 RPN 层选出一些候选区窗口供 fast rcnn 网络的训练和预测, 起加速作用. rpn 的核心是 anchor 机制, 针对经过卷积层之后 feature map 上的每一个点, 再次通过 3*3 的卷积融合包括其在内周围 9 个点的信息形成一个锚点特征向量 (对应下图中的 sliding window 的中心, 作者没有解释这样的做的原因, 推测是结果更具鲁棒性), 融合后维度保持不变仍然是 256 维向量, 随后以锚点为中心设置不同尺寸的 anchor box 以粗略选取原图对应的不同宽高比例, 不同大小的候选区域 (这样选取的结果不够精确, 所以需要两次 bbox 回归修正), 在 faster rcnn 中 anchor box 数量设置为 k=9. 通过引入 anchors 使得 faster rcnn 具备了多尺度映射.
RPN 层的处理流程:
- 原图像经过一个 5 层的卷积网络会得到 H*W*256 的 feature map, 在论文里的 H*W 是原图像的 ;
- 再次通过一个 3*3 的卷积层融合 sliding window 内的 9 个点形成锚点特征向量, 维度为 256 ;
- 以锚点为中心, 生成 k = 9 个 anchor box 作为初步的候选窗口, 个数 H*W*k;
- 锚点的特征向量经过两个分支进行训练: 一个分支是候选区域二分类, 经过 cls layer, 得到 2k(k组) 个得分, 每组两个值分别表示 foreground(anchor box 包含检测目标) 和 background(anchor box 不包含检测目标) 的概率; 另一个分支是 bbox regression, 经过 reg layer, 得到 4k(k组) 个输出, 每组的 4 维向量分别表示候选区域对应的四个坐标: .
其中, 每个 anchor 是否为真实区域取决于 IOU 的大小, 与任意一个真实区域的 IOU > 0.7 则为标定为正样本, 与所有真实区域的 IOU < 0.3 则标定为负样本. 使用所有的 anchors 训练过多, 实际训练中作者采用了 128 个正样本和 128 个负样本来训练. 测试阶段同时使用 NMS (threshold = 0.7) 去除冗余.
RPN 层和 fast rcnn 是共享特征图的, 只不过 RPN 层是利用 anchor 机制事先选择得到候选框. 这部分候选框映射到的特征图的区域经过 ROI pooling 得到相同大小的特征向量, 传递给后面两个分支: 全连接层分类和 bbox 回归.
7、Mask RCNN
终于到了正题 Mask RCNN, 这是我们在比赛中所使用的 state-of-art 模型, Kaiming He 大神在 2018 年初提出的模型.
mask rcnn 在 faster rcnn 工作的基础上提出的同时可以进行目标检测和目标分割的模型, 可以认为 Mask RCNN = ResNet-FPN + Fast RCNN + RoIAlign + Mask分支. 而 ResNet-FPN + Fast RCNN 实际上就是 Faster RCNN, 只不过 Faster RCNN 中只有一个 featrue map(conv5 对应的 featrue map), 而 ResNet-FPN 对应的是多个 feature map (conv2、conv3、conv4、conv5 以及对 conv5 的上采样的 feature map 构成的金字塔).
FPN
FPN (Feature Pyramid Networks, 特征金字塔网络) 的提出是为了解决图像的多尺度目标检测问题. 在目标检测中, 一个目标类别可能有很多种尺度, 比如在街头, 远处的行人和近处的行人尺度就相差很大. 这种多尺度问题用传统网络来做精度不够令人满意. 因为目标图像再经过深层卷积网络后, 语义信息增强但位置信息减弱, 所以最终检测效果会受到影响. 一个简单的思路是把目标图像进行多尺度缩放, 一起交给模型进行训练. 显然这种方法十分耗费计算资源和训练时间. 所以作者希望提出一种网络结构, 让图像在经过网络后既能提取出深层的语义信息, 也能保留原图的位置信息.
图像在经过一些卷积层和池化层后, 一般都是通道数变多, 特征图变小. 参见 resnet, Mask RCNN 中使用的 FPN 就是 ResNet-FPN:
可以看出, 卷积层 [conv2, conv3, conv4, conv5] 特征图每次长宽缩小为原来的 . 这个过程可以看成金字塔结构由底向上走, 图像的坐标信息逐渐减弱, 但语义信息随着通道数的增加而增强. 这就是 FPN 的第一阶段, 金字塔向上走的过程.
接下来是 FPN 的第二阶段:自顶向下——深层的特征图和浅层特征图通过横向连接融合. 如下图所示, 深(顶)层的特征图通过最近邻上采样 (不是反卷积, 减少了参数) 变换成原来的2倍, 下一层的特征图进行 1*1 卷积降低通道数保持和上层一致, 通过横向连接两层融合, 逐层往下走. 这个过程使图像的深层语义信息得以保留, 并且引入了底层的位置信息. 到了底部后, 再进行一次 3*3 的卷积, 去除上采样的混叠现象. 产生的特征金字塔经过 RPN 后金字塔的每层都会产生 RoI, 作者根据: 来选择使用哪一层生成的 RoI 作为最终的候选区域, 是尺度为 224*224 的 RoI 对应的层为第四层 feature map, 如果 RoI 的尺度为 112*112 则 意味着在第三层的 feature map 中取, 这个公式的含义是对于大尺度的 RoI 应该在低分辨率 feature map 中取有利于大目标的识别, 小尺度的 RoI 应该在高分辨率的 feature map 中取有利于小目标的检测. 这个设定十分合理, 因为小的目标经过多层卷积后基本无法辨别, 所以需要在高分辨率的层中取出.
RoI Align
mask rcnn 不仅需要检测候选框的类别, 还需要给出目标类别的 mask 映射. mask 映射应该遵循平移等价性, 就是说框移动, 输出的 mask 也会相应移动 (平移不变性是指框移动, 输出也不会变化, 比如分类器).
所以, 传统的 faster rcnn 在这一点上有着较大的缺陷, 因为它在卷积过程和 ROI 层经过了两次量化损失 (两次取整保证边界取值为整数). 比如说, 原图像 256*256, 而特征图是 16*16, 缩小为原来的 . 那么原图中坐标 17 映射到特征图上就是 1.0625, 在这里小数部分会被舍去. 而在 ROI 层, 如果框的大小是 15*15, 要进行 7*7 的 pooling, 又是一次损失. 这种损失会导致生成的 mask 和目标无法正确对齐 (mis-alignment). 所以, RoIAlign 为了得到浮点数坐标的像素值, 利用了双线性插值算法. 像下图种每个 bin 里面的四个点 (采样点为 4) 的像素值, 都是由这种方法得到的, 之后再进行正常的 max pooling 操作.
mask rcnn structure
mask rcnn 和 faster rcnn 除了 RoIAlign 的区别外, 另一个区别就是加了一个 mask 的分支, 如下图:
以 ResNet 为例:候选区域经过 RoIAlign 层得到 7*7*2048 的 feature map, mask 分支是通过 FCN 网络来实现的, 最终得到了 14*14*80 的输出, 这里的 80 就是类别数, 也就是有 80 个 mask, 但只有一个是对应于目标类别的. 所以需要等另一个分支预测得到区域类别后选择对应的 mask 输出.
8、yolo
Yolo(You Only Look Once: Unified, real-time object detection) 是 one-stage (rcnn 系列模型先挑选 region proposal, 然后分类回归属于 two-stage)目标检测的主流方法, 虽然精度比 rcnn 系列差但是速度比 rcnn 系列模型要快很多, 在一些注重速度的目标检测系统中经常会用到.
yolo v1
输入图片被 resize 成 448*448 的图片, 随后被划分成 S*S 的网格 (论文中 S=7), 经过如下的 CNN 网络结构后输出了 S*S*N 的向量, 每个 N 维向量对应于一个单元格的预测信息. 其中 N=B*5+C, B 是人为设置的要预测每个单元格内需要预测的 bbox 数量, C 是目标类别数 (论文中 B=2 和 C=20), 所以 N=30.
每个单元格实际上预测是 bbox 的置信度 (confidence score), 这个置信度包含两个方面, 一是 bbox 内包含检测目标的可能性 ; 二是 bbox 的准确度即 bbox 和 ground truth 边界框的 IOU 值. 因此最终置信度得分为:
这里记录几个关键点:
bbox 的位置信息是一个 5 维向量:, 类别预测向量是 20 维. 坐标 代表了预测的 bbox 的中心坐标, 这个中心坐标是相对于单元格左上角而言的偏移值. 坐标 代表了预测的 bbox 的 width、height 相对于整幅图像 width、height 的比例. 则是置信度得分.
ground truth box 只有一个, 要同时预测多个 bbox 信息的目的是起到一种集成的作用. 这些 bbox 位置大小信息的输出在训练过程中都是以同一个 ground truth box 的信息来计算 loss 的. 但这也恰恰是 Yolo 的缺点之一, 一个单元格内不管设置了多少个 bbox 都只预测一组所属类别概率, 到了 Yolo v2 之后类别概率和 bbox 一一对应.
输出类别概率的公式为:
因为每个格子都会给出两个 bbox 的预测, 所以在一张图片中每个目标类别会得到 7*7*2 个 bbox 和概率值. 要得到最终的 bbox 还得经过一个后处理步骤: NMS (非极大值抑制, 去除冗余 bbox), NMS 首先丢弃分类概率小于预定值的 bbox, 然后对不同 bbox 计算得到的分类概率排序, 选择最大概率的 bbox 假设是 A 加入最终检测结果, 判断剩余的 bbox, 只要其与 A 的 IOU 大于预设阈值就丢弃, 对于其他 objec 的 bbox 重复上述过程直到候选框数量为0.
9、SSD
除了 yolo, SSD(Single Shot MultiBox Detector)是目标检测领域 one-stage 方法的另一主流框架, 和 yolo 的区别如下:
1、yolo 只用最高层的特征图 (7*7*1024) 来进行预测, 这样做速度很快但在检测小物体上就不尽人意. SSD 效仿了特征金字塔的思路, 在 6 种尺度的特征图上进行检测, 大尺度特征图 (较靠前的特征图) 可以用来检测小物体, 而小尺度特征图 (较靠后的特征图) 用来检测大物体;
2、SSD 还加入了先验框 Prior box (降低训练难度), 类似于 RPN 的 anchor box, 区别于 yolo, SSD 为每个单元格的所有先验框都输出独立的检测值 (分为两部分, 一是属于各类别的置信度, 二是先验框的坐标信息).
structure
SSD 的网络结构如下, 和 yolo 一样, SSD 也是只使用 CNN(基于 VGG-16, 把 fc6、fc7 换成了 3*3 和 1*1 的conv6、conv7) 提取特征, 和 yolo 不同的在于 yolo 经过一个全连接层后检测目标, SDD 直接用卷积检测目标. 图中的 conv4_3、conv7、conv8_2、conv9_2、conv10_2 和 conv11_2 是 6 个不同尺度的特征图.
每一个特征图又会经过 3 条线路:
1、生成先验框, 除了 conv4_3 固定使用 4 个先验框, 后面 5 个特征图根据公式生成.
2、经过 bn 层 + 一次 3*3 卷积后进行 softmax 分类, 预测目标类别的置信度;
3、经过 bn 层 + 一次 3*3 卷积后生成 bbox 位置特征用于后面的 bbox regression.
Prior box
SSD 按照如下规则生成 prior box:
以 feature map 上每个点为中心, 生成一系列同心的 prior box
正方形 prior box 最小边长和最大边长为:
长方形 prior box 最小边长和最大边长为 (aspect ratio 是人为设置的):
每个 feature map 对应的 min_size 和 max_size 由以下公式决定:
其中, 表示先验框相对于输入图尺寸 (300*300) 的缩放度, 指预测时 feature map 的数量, (conv4_3 不在其中, 该层单独设置 4 个 prior box), 是人为设置的
例如:
对于 conv4_3: k = 1, min_size = s1*300, max_size = s2*300
对于 conv-7: k = 2, min_size = s2*300, max_size = s3*300
10、Focal Loss
Focal loss 是针对交叉熵在样本不平衡的场景下做出的改进, 简单来说就是给样本加了一个能随分类难度变化而变化的权重, 从而让易分类的样本对 loss 的贡献变小, 让难分类样本和数量少的类别对 loss 的贡献变大.
上面的公式是交叉熵损失 (对于类别 t 的 loss), 是目标检测中常用的损失函数. 对于 two-stage 方法而言, 这个损失函数并没有什么不足因为有 RPN 进行初步的候选区域采样. 所以对于后面的网络, 正负样本比例不会有严重失衡的情况. 但对于 one-stage 方法来说, 采样得到的样本框大部分都是背景, 这种情况下负样本占了 loss 的大部分贡献, 会影响正样本的分类性能.
一个直观的方法是在样本少的类别前面加上一个大于 1 的权重:
但这种办法只能解决样本少的类别问题, 并没有解决样本分类难易的问题. 因此有了如下的形式:
对于预测概率 $p_t$ 高的样本, Focal loss 认为它是容易分类的样本, 因此给的权重就比较低. 而对于预测概率比较低的样本, Focal loss认为它们难以分类, 给的权重也比较高; 所以模型会侧重训练这些样本. 综合以上两种形式可得:
Focal loss 的 pytorch 简单实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22# Definition
class FocalLoss(nn.Module):
def __init__(self, gamma=2, size_average=True):
super(FocalLoss, self).__init__()
self.gamma = gamma
self.size_average = size_average
def forward(self, y_pred, y_true):
p = Sigmoid(y_pred)
p[y_true==0] = 1 - p[y_true==0]
log_p = p.log()
batch_loss = -torch.pow((1-p),self.gamma)*log_p
if self.size_average:
loss = batch_loss.mean()
else:
loss = batch_loss.sum()
return loss
# Usage:
loss_fn = FocalLoss(gamma=2)
loss = loss_fn(y_pred, y_batch)
optimizer.zero_grad()
loss.backward()
总结
为什么选用 Mask RCNN 这种 two-stage 模型而不采用 one-stage 模型?
旷视科技的俞刚博士提到: one-stage 和 two-stage 检测器之间的本质区别在于检出率 (recall) 与定位 (localization) 之间的权衡 (tradeoff). Recall 是指如果一张图像上有 100 个物体, 检测到 99 个, 那么 recall 为 99%; Localization 则是指边界框框住物体的空间上的精度. 一般来讲, one-stage 检测器的 recall 较高, 但是 localization 会有所折衷; 相反, two-stage 检测器的定位能力则较强, 但是 recall 较低, 主要原因是第二个 stage 可以 refine 框的精度, 但是也会误杀一些正样本. 但是大家普遍会觉得 one-stage 检测器往往更快, 比如 YOLO、SSD; two-stage 检测器往往更准, 比如 Faster R-CNN、FPN, 但这个事情事实上并不成立.
第一, one-stage 算法从网络结构上看只是一个多分类的 rpn 网络, 相当于 faster rcnn 的第一阶段. 其预测结果是从 feature map 中 anchor 对应的特征中预测得到的, 而 two-stage 会对上述结果再进行 roi pooling 之后进一步精细化, 因此更为精准. 比如, 一个 anchor 有可能只覆盖了一个目标的 50%, 但却作为完全的正样本, 因此其预测肯定是有误差的.
第二, one-stage 算法对小目标检测效果较差, 如果所有的 anchor 都没有覆盖到这个目标, 那么这个目标就会漏检. 如果一个比较大的 anchor 覆盖了这个目标, 那么较大的感受野会弱化目标的真实特征, 得分也不会高. two-stage 算法中的 roi pooling 会对目标做 resize, 小目标的特征被放大, 其特征轮廓也更为清晰, 因此检测也更为准确.
基于上述描述, 考虑到我们的比赛是对 bbox 的坐标进行编码后计算 bbox 准确度, 因此我们更关注 bbox 的定位准确, 所以采用了 two-stage 模型.